/**
 * @file 类型相关api
 */

/**
 * 判断值是否为 null
 * @param {unknown} value
 * @returns {value is null}
 *
 * @example
 * isNull(null) // => true
 * isNull('') // => false
 */
export const isNull = (value?: unknown): value is null => {
  return Object.prototype.toString.call(value) === '[object Null]'
}

/**
 * 判断值是否为 undefined
 * @param {unknown} value
 * @returns {value is undefined}
 *
 * @example
 * isUndefined(undefined) // => true
 * isUndefined('') // => false
 */
export const isUndefined = (value?: unknown): value is undefined => {
  return Object.prototype.toString.call(value) === '[object Undefined]'
}

/**
 * 判断值是否为 Number
 * @param {unknown} value
 * @returns {value is number}
 *
 * @example
 * isNumber(123) // => true
 * isNumber('') // => false
 */
export const isNumber = (value?: unknown): value is number => {
  return Object.prototype.toString.call(value) === '[object Number]'
}

/**
 * 判断值是否为 Boolean
 * @param {unknown} value
 * @returns {value is boolean}
 *
 * @example
 * isBoolean(true) // => true
 * isBoolean(1) // => false
 */
export const isBoolean = (value?: unknown): value is boolean => {
  return Object.prototype.toString.call(value) === '[object Boolean]'
}

/**
 * 判断值是否为 String
 * @param {unknown} value
 * @returns {value is string}
 *
 * @example
 * isString('1') // => true
 * isString(1) // => false
 */
export const isString = (value?: unknown): value is string => {
  return Object.prototype.toString.call(value) === '[object String]'
}

/**
 * 判断值是否为 Object
 * @param {unknown} value
 * @returns {value is object}
 *
 * @example
 * isObject({}) // => true
 * isObject(1) // => false
 */
export const isObject = <T extends Record<any, any>>(value?: T | unknown): value is T => {
  return Object.prototype.toString.call(value) === '[object Object]'
}

/**
 * 判断值是否为 Array
 * @param {unknown} value
 * @returns {value is unknown[]}
 *
 * @example
 * isArray([]) // => true
 * isArray({}) // => false
 */
export const isArray = <T extends unknown[]>(value?: T | unknown): value is T => {
  return Object.prototype.toString.call(value) === '[object Array]'
}

/**
 * 判断值是否为 Function
 * @param {unknown} value
 * @returns {value is Function}
 *
 * @example
 * isFunction(()=>{}) // => true
 * isFunction(1) // => false
 */
export const isFunction = (value?: unknown): value is Function => {
  return Object.prototype.toString.call(value) === '[object Function]'
}

/**
 * 判断值是否为 Date
 * @param {unknown} value
 * @returns {value is Date}
 *
 * @example
 * isDate(new Date()) // => true
 * isDate(1123234234234) // => false
 */
export const isDate = (value?: unknown): value is Date => {
  return Object.prototype.toString.call(value) === '[object Date]'
}

/**
 * 判断值是否为 正则 RegExp
 * @param {unknown} value
 * @returns {value is Date}
 *
 * @example
 * isRegExp(new RegExp()) // => true
 * isRegExp(/a-z/) // => true
 * isRegExp(1) // => false
 */
export const isRegExp = (value?: unknown): value is RegExp => {
  return Object.prototype.toString.call(value) === '[object RegExp]'
}

/**
 * 判断值是否为 Symbol
 * @param {unknown} value
 * @returns {value is symbol}
 *
 * @example
 * isSymbol(Symbol()) // => true
 * isSymbol('') // => false
 */
export const isSymbol = (value?: unknown): value is symbol => {
  return Object.prototype.toString.call(value) === '[object Symbol]'
}

/**
 * 判断值是否为一个可解析的JSON字符串
 * @param {unknown} value
 * @returns {value is string}
 *
 * @example
 * isJsonString('[]') // => true
 * isJsonString('') // => false
 */
export const isJsonString = (value?: unknown): value is string => {
  if (!isString(value)) return false

  try {
    const obj = JSON.parse(value)
    return typeof obj == 'object' && obj
  } catch (error) {
    return false
  }
}

/**
 * 判断值是否为空，包含 undefined、null、空字符串（只有空格的字符串）、空数组、空对象
 * @param {unknown} value 要判断的数据
 * @returns {value is void}
 *
 * @example
 * isEmpty('   ') // => true
 * isEmpty({}) // => true
 * isEmpty(0) // => false
 */
export const isEmpty = (value?: unknown): value is void => {
  if (
    isUndefined(value) ||
    isNull(value) ||
    (isString(value) && value.replace(/(^\s*)|(\s*$)/g, '').length === 0) ||
    (isObject(value) && !Object.keys(value).length) ||
    (isArray(value) && !value.length)
  ) {
    return true
  }
  return false
}

/**
 * 判断值是否不为空，与isEmpty取反
 * @param {unknown} value 要判断的数据
 * @returns {boolean}
 *
 * @example
 * isNotEmpty(0) // => true
 * isNotEmpty('   ') // => false
 * isNotEmpty({}) // => false
 */
export const isNotEmpty = <T>(value?: unknown): value is T => {
  return !isEmpty(value)
}
